with Ada.Directories;
with Ada.Text_IO;
with Ada.Float_Text_IO;
with Legend.Traces;
with Legend.Grps;
with Interfaces.C; use Interfaces.C;
with Performances;
with Legend.Renders;
with Legend.Stages.Rooms;
package body Legend.Resources is

   Default_Palette : Palette_Access;
   -- 调色板

   function Block_Count (
      Block_Size : Stream_IO.Count;
      Type_Size  : Stream_IO.Count
      ) return Uint16_t
   is
      use type Stream_IO.Count;
   begin
      return Uint16_t(Block_Size / (Type_Size / 8));
   end Block_Count;

   function Load_Palette (
      Filename : String
      ) return Palette_Access
   is
      palette : Palette_Access := new Palette_256;

      procedure On_Load (FS : not null access IO_Utils.Stream_Class) 
      is
         brightness : constant := 4;
         -- 令人惊讶的是按BGR的次序进行读取的显示结果跟RGB显示结果一致, why?
         -- 然而调色板中的R,B项多数并不相同
      begin
         for p of palette.all loop
            p := (
            R => Uint8_t'Input(fs) * brightness,
            G => Uint8_t'Input(fs) * brightness,
            B => Uint8_t'Input(fs) * brightness,
            A => Uint8_t'Last -- 不透明;
            );
         end loop;
      end On_Load;
   begin
      IO_Utils.Read_File_Stream(JY_path & Filename, On_Load'Access);
      return palette;
   end Load_Palette;

   function Load_Pictures (
      Index_Filename : String;
      Group_Filename : String
      ) return Pictures_Pointer
   is
      pictures : Pictures_Pointer;
      reader : Grps.Group_File_Type;
      pm : Performances.Monitor_Type;
   begin
      reader.Open(JY_Path & Index_Filename, JY_Path & Group_Filename);
--      Trace_Log(
--         "loading " & Uint16_t'Image(reader.Index_Count) &
--         " pictures from " & Group_Filename);
      pictures := new Pictures_Type(0 ..  reader.Index_Count - 1);
      pm.Start;
      -- 值得注意的是, 图片并非按索引顺序存放的
      for p of pictures.all loop
         reader.Next_Block;
         if not reader.End_Of_Data then
            Read_Picture(reader.Data_Stream, reader.Block_Size, p);
         end if;
      end loop;
      pm.Stop;
--      Trace_Log(Stdout_GBK("耗时") & Duration'Image(pm.Consumed));
      reader.Close;
      return pictures;
   end Load_Pictures;

   -- 加载关卡资源(sdx???, smp??? 或wdx???, wmp???)
   procedure Load_Stage_Pictures (
      Pictures_Array : in out Pictures_Array_Type; 
      Index          : Uint16_t; 
      File_Prefix    : String
      )
   is
   begin
      if Index not in Pictures_Array'Range then
         return;
      end if;
      if Pictures_Array(Index) /= null then
         return;
      end if;
      declare
         s : String := Uint16_t'Image(Index + 1000);
         idx : String renames s(s'Last - 2 .. s'Last);
         head_name : String := File_Prefix & "DX" & idx;
         body_name : String := File_Prefix & "MP" & idx;
      begin
         Pictures_Array(Index) := Load_Pictures(head_name, body_name);
      end;
   end Load_Stage_Pictures;

   procedure Load_Room_Pictures (Index : Uint16_t)
   is
   begin
      Load_Stage_Pictures(Inner_Images, Index, "S");
   end Load_Room_Pictures;

   procedure Load_War_Pictures (Index : Uint16_t)
   is
   begin
      Load_Stage_Pictures(War_Images, Index, "W");
   end Load_War_Pictures;

   -- mmap
   -- without DisplayFormatAlpha : 0.727, 0.690, 0.679
   -- with DisplayFormatAlpha : 0.717, 0.711, 0.716

   procedure Read_Picture (
      From : not null access Root_Stream_Type'Class;
      Size : Stream_IO.Count;
      P    : out Picture_Type
      )
   is
      pragma Suppress (Range_Check); 
      pragma Suppress (Index_Check); 
      -- ^对性能影响甚微, 载入mmap.grp总体提升1毫秒
      procedure Init_Picture 
      is
         use type SDL.Video.Surface_Flags_t;
         w, h : Uint16_t;
      begin
         w := Uint16_t'Input(From);
         h := Uint16_t'Input(From);
         p.Position := Point_Type'Input(From);
         p.Surface := Renders.Create_SDL_Surface(w, h);
      end Init_Picture;
      
      procedure Draw_Picture
      is
         type Picture_Line is array (0 .. P.Surface.W - 1) of Color_Type;
         Pixels : array (0 .. p.Surface.H - 1) of Picture_Line 
         with Address => System.Address(p.Surface.Pixels);
         pragma Import (Ada, Pixels);

         cy, cx     : int := 0;

         -- 使用调色板
         type Origin_Line_Type is array (Uint8_t range <>) of Uint8_t;
         -- 使用真彩
         type Target_Line_Type is array (Uint8_t range <>) of Uint32_t;

         procedure Draw_Line 
         is
            n, n_bg : int;
            function Read_Byte return Uint8_t 
            is
               pragma Inline (Read_Byte);
            begin
               n := n - 1;
               return Uint8_t'Input (From);
            end Read_Byte;

            procedure Draw_Slice
            is
               pragma Inline (Draw_Slice);
               src : Origin_Line_Type (1 .. Read_Byte);
               dst : Target_Line_Type (src'Range) 
               with Address => Pixels(cy)(cx)'Address;
               pragma Import (Ada, dst);
               --	       i : Uint8_t := 1;
            begin
               Origin_Line_Type'Read(From, src);
               -- 索引并未参与运算, 编译器能将寻址运算中乘法优化掉吗?
               -- 如果只有一个数组时, 编译器可以产生指针加法代码
               -- 但多数组时, 似乎只能通过索引中的乘法运算寻址了
               for i in dst'Range loop 
                  declare
                     dc : Color_Type renames Default_Palette(src(i));
                  begin
                     dst(i) := SDL.Video.Map_RGBA(
                        Fmt => p.Surface.Format,
                        R => dc.R,
                        G => dc.G,
                        B => dc.B,
                        A => dc.A);
                  end;
               end loop;
               cx := cx + src'Length;
               n := n - src'Length;
            end Draw_Slice;
         begin
            n := int(Uint8_t'Input(From));
            -- means following bytes
            -- Trace_Log ("n = " & n'Img);
            cx := 0;
            while n > 0 loop
               n_bg := int(Read_Byte);
               cx := cx + n_bg;
               Draw_Slice;
            end loop;
            cy := cy + 1;
         end Draw_Line;

         procedure Clear 
         is
            pragma Inline (Clear);
         begin
            SDL.Video.Fill_Rect(p.Surface, null, 0);
            -- Pixels := (others => (others => (others => 0)));
         end Clear;
      begin
         Clear;
         while cy < p.Surface.H loop
            Draw_Line;
         end loop;
      end Draw_Picture;
   begin
      Init_Picture;
      Draw_Picture;
      -- Trace_Log ("before return " & Image (p.Position));
   end Read_Picture;

   -- *.big
   -- 320 * 200
   function Read_Big_Picture (Name : String) return Picture_Type
   is
      Big_Width  : constant := 320;
      Big_Height : constant := 200;
      subtype Big_Type is Stream_Element_Array(1 .. Big_Width * Big_Height);
      f : Stream_IO.File_Type;
      fs : Stream_IO.Stream_Access;
      result : Picture_Type;
   begin
      result.Surface := Renders.Create_SDL_Surface(Big_Width, Big_Height);
      Stream_IO.Open(File => f, Name => Name, Mode => Stream_IO.In_File);
      fs := Stream_IO.Stream(f);
      declare
         src : Big_Type;
         i : Positive := 1;
         dst : array(Positive range 1 .. Big_Width * Big_Height) of Uint32_t 
         with Address => System.Address(result.Surface.Pixels);
         pragma Import (Ada, dst);
      begin
         Big_Type'Read(fs, src);
         for byte of src loop
            declare
               dc : Color_Type renames Default_Palette(Uint8_t(byte));
            begin
               dst(i) := SDL.Video.Map_RGBA(
                  Fmt => result.Surface.Format,
                  R => dc.R,
                  G => dc.G,
                  B => dc.B,
                  A => dc.A);
               i := i + 1;
            end;
         end loop;
      end;
      Stream_IO.Close(f);
      return result;
   end Read_Big_Picture;


   function Load_Talks (Filename : String := "TALK") return Talks_Pointer
   is
      result : Talks_Pointer;
      reader : Grps.Group_File_Type;

      function "xor" (C : Character; B : Uint8_t) return Character
      is
         pragma Inline("xor");
      begin
         return Character'Val(B xor Character'Pos(C));
      end "xor";

      procedure Read_Talk (Ptr : in out String_Access)
      is
         string_length : Natural := Natural(reader.Block_Size) - 1;
         s : String(1 .. String_Length);
      begin
         String'Read(reader.Data_Stream, s);
         for c of s loop
            c := c xor 16#FF#;
         end loop;
         ptr := new String'(BIG5_To_UTF8.Convert(s));
      end Read_Talk;
   begin
      reader.Open(JY_Path & Filename & ".IDX", JY_Path & Filename & ".GRP");
      result := new Talks_Type(0 .. reader.Index_Count - 1);
      -- Trace_Log(Stdout_GBK("共" & reader.Index_Count'Img & " 个对话"));
      for p of result.all loop
         reader.Next_Block;
         Read_Talk(p);
      end loop;
      reader.Close;
--      Traces.Export_Talks(result);
      return result;
   end Load_Talks;

   -- 加载战斗事件定义
   function Load_War_Defines (
      Filename : String := "WAR.STA"
      ) return War_Defines_Pointer
   is
      result : War_Defines_Pointer;
      procedure Read_Data (Data_File : in out Stream_IO.File_Type)
      is
         use type Stream_IO.Count;
         n : Uint16_t := Block_Count(Stream_IO.Size(Data_File), 186 * 8);
         -- War_Define_Type'Max_Size_In_Storage_Elements = 194
         -- War_Define_Type 含两个War_Hero_List.Count, 需减之
         -- ^ maybe a gnat implention bug?
         -- War_Define_Type'Size - 32 * 2 = 186 * 8
         subtype War_Define_List is War_Defines_Type (0 .. n - 1);
      begin
         result := new War_Define_List;
         War_Define_List'Read (Stream_IO.Stream(Data_File), result.all);
      end Read_Data;
   begin
      IO_Utils.Read_File(JY_Path & Filename, Read_Data'Access);
--      Traces.Export_War_Defines(result);
      return result;
   end Load_War_Defines;

   procedure Load_War_Define_Resources
   is
   begin
      All_War_Defines := Load_War_Defines;
   end Load_War_Define_Resources;

   function Read_Kongfus (
      From : not null access Root_Stream_Type'Class;
      Count : Uint16_t
      ) return Kongfus_Pointer
   is
      subtype Kongfu_List is Kongfus_Type(0 .. Count - 1);
      result : Kongfus_Pointer;
   begin
      result := new Kongfu_List;
      Kongfu_List'Read(From, result.all);
      return result;
   end Read_Kongfus;

   function Read_Scenes (
      From : not null access Root_Stream_Type'Class;
      Count : Uint16_t
      ) return Scenes_Pointer
   is
      result : Scenes_Pointer;
      subtype Scene_List is Scenes_Type (0 .. Count - 1);
   begin
      result := new Scene_List;
      Scene_List'Read (From, result.all);
      return result;
   end Read_Scenes;

   function Read_Items (
      From : not null access Root_Stream_Type'Class;
      Count : Uint16_t
      ) return Items_Pointer
   is
      result : Items_Pointer;
      subtype Item_List is Items_Type(0 .. Count - 1);
   begin
      result := new Item_List;
      Item_List'Read (From, result.all);
      return result;
   end Read_Items;


   function Read_Heroes (
      From : not null access Root_Stream_Type'Class;
      Count : Uint16_t
      ) return Heroes_Pointer
   is
      result : Heroes_Pointer;
      subtype Hero_List is Heroes_Type(0 .. Count - 1);
   begin
      result := new Hero_List;
      Hero_List'Read(From, result.all);
      return result;
   end Read_Heroes;

   function Read_Shops (
      From : not null access Root_Stream_Type'Class;
      Count : Uint16_t
      ) return Shops_Pointer
   is
      result : Shops_Pointer;
      subtype Shop_List is Shops_Type(0 .. Count - 1);
   begin
      result := new Shop_List;
      Shop_List'Read(From, result.all);
      return result;
   end Read_Shops;

   procedure Load_Talk_Resources
   is
   begin
      All_Talks := Load_Talks;
   end Load_Talk_Resources;

   procedure Load_Palette_Resources
   is
   begin
      Default_Palette := Load_Palette("MMAP.COL");
   end Load_Palette_Resources;

   procedure Load_Title_Resources
   is
   begin
      Title_Images := Load_Pictures("TITLE.IDX", "TITLE.GRP");
      Title_Image := Read_Big_Picture(JY_Path & "TITLE.BIG");
   end Load_Title_Resources;

   procedure Load_Fight_Picture (I : Uint16_t) 
   is
      ps : Pictures_Pointer renames Fight_Pictures(I);
   begin
      if Fight_Pictures(I) /= null then
         return;
      end if;
      declare
         idx : String := Uint16_t'Image(i + 1000);
         name : String := "FIGHT" & idx(idx'First + 2 .. idx'Last);
         head_name : String := name & ".IDX";
         body_name : String := name & ".GRP";
      begin
         if Ada.Directories.Exists(JY_Path & head_name) then
            ps := Load_Pictures(head_name, body_name);
            Trace_Log(ps'Length'Img & " frames loaded");
         end if;
      end;
   end Load_Fight_Picture;

   procedure Load_Picture_Resources 
   is
   begin
      Dead_Image := Read_Big_Picture(JY_Path & "DEAD.BIG");
      Head_Images := Load_Pictures("HDGRP.IDX", "HDGRP.GRP");
      for p of Head_Images.all loop
         Renders.Set_To_Opaque(p);
      end loop;
      Outer_Images := Load_Pictures("MMAP.IDX", "MMAP.GRP");
      Cloud_Images := Load_Pictures("CLOUD.IDX", "CLOUD.GRP");
--      FBK_Images := Load_Pictures("FBK.IDX", "FBK.GRP");
--      FMap := Load_Pictures("FMAP.IDX", "FMAP.GRP");
      Shadow_Picture.Surface := Renders.Make_Shadow(
         Color => (A => 64, others => 0), 
         Width => 36, Height => 18);
      Locator_Picture.Surface := Renders.Make_Shadow(
         Color => (A => 128, others => 0), 
         Width => 36, Height => 18);
      -- Export_Warfld;
   end Load_Picture_Resources;

   function Get_Fire_Script_By_Hero (Who : Uint16_t) return Uint16_t
   is
   begin
      case Who is
         when 1      => return 950;  -- 胡斐
         when 2      => return 952;  -- 程灵素
         when 9      => return 954;  -- 张无忌
         when 16     => return 956;  -- 胡青牛
         when 17     => return 958;  -- 王难姑
         when 25     => return 960;  -- 蓝凤凰
         when 28     => return 962;  -- 平一指
         when 29     => return 964;  -- 田伯光
         when 35     => return 966;  -- 令狐冲
         when 36     => return 968;  -- 林平之
         when 37     => return 970;  -- 狄云
         when 38     => return 972;  -- 石破天
         when 44     => return 974;  -- 岳老三
         when 45     => return 976;  -- 薛慕华
         when 47     => return 978;  -- 阿紫
         when 48     => return 980;  -- 游坦之
         when 49     => return 982;  -- 虚竹
         when 51     => return 984;  -- 慕容复
         when 53     => return 986;  -- 段誉
         when 54     => return 988;  -- 袁承志
         when 58     => return 990;  -- 杨过
         when 59     => return 992;  -- 小龙女
         when 61     => return 994;  -- 欧阳克
         when 63     => return 996;  -- 程瑛
         when 76     => return 998;  -- 王语嫣
         when others => return No_Script;
      end case;
   end Get_Fire_Script_By_Hero;

   procedure Export_Pictures (Pictures : Pictures_Pointer; Path : String)
   is
      dummy : Interfaces.C.int;
   begin
      if not Ada.Directories.Exists(Path) then
         Ada.Directories.Create_Path(Path);
      end if;
      if Pictures = null then
         return;
      end if;
      -- export pictures => bmps
      for i in Pictures'Range loop
         declare
            p : Picture_Type renames Pictures(i);
            s : String := Uint16_t'Image(10000 + i);
            fn : String := Path & s(3..s'Last) & ".bmp";
         begin
            exit when p.Surface in null;
            dummy := SDL.Video.Save_BMP(p.Surface, fn);
         end;
      end loop;
      Trace_Log("ok");
   end Export_Pictures;

   procedure Load_World_Record (
      From : String;
      To   : in out World_Record_Pointer
      )
   is
      result : World_Record_Pointer := new World_Record_Type;
      reader : Grps.Group_File_Type;
      data_stream : Stream_IO.Stream_Access renames reader.Data_Stream;

      function Number_Of (type_size : Stream_IO.Count) return Uint16_t 
      is
      begin
         return Block_Count(reader.Block_Size, type_size);
      end Number_Of;
   begin
      Trace_Log("loading world " & From);
      reader.Open(JY_Path & From & ".IDX", JY_Path & From & ".GRP");
      -- 0 => 0000_0344, Leader_State_type => 836 bytes
      -- 1 => 0000_e6c4, Heroes        => 182bytes * 320 = 58240 bytes
      -- 2 => 0001_7b34, Items         => 190bytes * 200 = 38000 bytes
      -- 3 => 0001_8c44, Scenes        => 52bytes * 84 = 4368 bytes
      -- 4 => 0001_bdac, Kongfus       => 136bytes * 93 = 12648bytes
      -- 5 => 0001_be42, Shops         => 30bytes * 5 = 150 bytes
      -- 基本信息
      reader.Next_Block;
      Leader_State_type'Read(data_stream, result.Leader);
      -------------------------------------------------------------------
      -- 人物属性(320位)
      reader.Next_Block;
      result.Heroes := Read_Heroes(data_stream, Number_Of(Hero_Type'Size));
      -------------------------------------------------------------------
      -- 物品原型清单(200种)
      reader.Next_Block;
      result.Items := Read_Items(data_stream, Number_Of(Item_Type'Size));
      -------------------------------------------------------------------
      -- 场景清单(84项)
      reader.Next_Block;
      -- result.Scenes := Read_Scenes(data_stream, 84);
      -- Scene_Type含有Coordinates_Type类型, 其长度包括Count长度, 需减之
      result.Scenes := Read_Scenes(data_stream, Number_Of(Stream_IO.Count(Scene_Type'Size - 32)));
      -------------------------------------------------------------------
      -- 武功清单(93项)
      reader.Next_Block;
      result.Kongfus := Read_Kongfus(data_stream, Number_Of(Kongfu_Type'Size));
      -------------------------------------------------------------------
      -- 商店信息(5项)
      reader.Next_Block;
      result.Shops := Read_Shops(data_stream, Number_Of(Shop_Type'Size));
      reader.Close;
      Traces.Trace_Profile(result.all);
--      Traces.Export_Heroes(result.all);
--      Traces.Export_Kongfus(result.Kongfus.all);
--      Traces.Export_Scenes(result.Scenes.all);
      if To /= null then
         Dispose(To.Kongfus);
         Dispose(To.Items);
         Dispose(To.Heroes);
         Dispose(To.Shops);
         Dispose(To);
      end if;
      To := result;
      Trace_Log("loaded world " & From);
   end Load_World_Record;


   procedure Load_Scene_Maps (
      From : String; 
      To   : in out Scene_Maps_Pointer 
      )
   is
      reader : Grps.Group_File_Type;
      result : Scene_Maps_Pointer;
   begin
      Trace_Log("loading scene maps " & From);
      reader.Open(JY_Path & From & ".idx", JY_Path & From & ".grp");
      result := new Scene_Maps(0 .. reader.Index_Count - 1);
      for map of result.all loop
         reader.Next_Block;
         for layer of map(ml_Earth .. ml_BY) loop
            layer := new Stages.Rooms.Room_Layer_Type;
            Layer_Type'Read(reader.Data_Stream, layer.all);
         end loop;
      end loop;
      reader.Close;
      if To /= null then
         for map of To.all loop
            for layer of map loop
               if layer /= null then
                  Dispose(layer);
               end if;
            end loop;
         end loop;
         Dispose(To);
      end if;
      To := result;
      Trace_Log("loaded scene maps " & From);
   end Load_Scene_Maps;

   procedure Load_Scene_Stories (
      From : String; 
      To   : in out Scene_Stories_Pointer
      )
   is
      result : Scene_Stories_Pointer;
      reader : Grps.Group_File_Type;
      number : Uint16_t;
   begin
      Trace_Log("loading scene stories " & From);
      reader.Open(JY_path & From & ".IDX", JY_path & From & ".GRP");
      result := new Scene_Stories_Type(0 .. reader.Index_Count - 1);
      for s of result.all loop
         reader.Next_Block;
         exit when reader.End_Of_Data;
         number := Block_Count(reader.Block_Size, Stream_IO.Count(22 * 8)); 
         s := new Stories_Type(0 .. number - 1);
         Stories_Type'Read(reader.Data_Stream, s.all);
      end loop;
      reader.Close;
      if To /= null then
         for s of To.all loop
            Dispose(s);
         end loop;
         Dispose(To);
      end if;
      To := Result;
      Trace_Log("loaded scene stories " & From);
   end Load_Scene_Stories;


   procedure Save_World_Record (From : World_Record_Pointer; To : String)
   is
      Path : String := JY_Path;
      file : Grps.Group_File_Type;
   begin
      Trace_Log("storing world record " & To);
      file.Open(Path & To & ".IDX", Path & To & ".GRP");
      file.Set_Writable;
      -- 0 => 0000_0344, Leader_State_type => 836 bytes
      -- 1 => 0000_e6c4, Heroes        => 182bytes * 320 = 58240 bytes
      -- 2 => 0001_7b34, Items         => 190bytes * 200 = 38000 bytes
      -- 3 => 0001_8c44, Scenes        => 52bytes * 84 = 4368 bytes
      -- 4 => 0001_bdac, Kongfus       => 136bytes * 93 = 12648bytes
      -- 5 => 0001_be42, Shops         => 30bytes * 5 = 150 bytes
      -- 基本信息
      file.Next_Block;
      Leader_State_type'Write(file.Data_Stream, From.Leader);
      -------------------------------------------------------------------
      -- 人物属性(320位)
      file.Next_Block;
      Heroes_Type'Write(file.Data_Stream, From.Heroes.all);
      -------------------------------------------------------------------
      -- 物品原型清单(200种)
      file.Next_Block;
      Items_Type'Write(file.Data_Stream, From.Items.all);
      -- if Stream_IO.Index(file.Data_File) not in 16#0001_7b35# then
      --    Trace_Log("warnning, item size not matched");
      -- end if;
      -------------------------------------------------------------------
      -- 场景清单(84项)
      file.Next_Block;
      -- result.Scenes := Read_Scenes(data_stream, 84);
      -- Scene_Type含有Coordinates_Type类型, 其长度包括Count长度, 需减之
      Scenes_Type'Write(file.Data_Stream, From.Scenes.all);
      -- if Stream_IO.Index(file.Data_File) not in 16#0001_8c45# then
      --    Trace_Log("warnning, scene size not matched");
      -- end if;
      -------------------------------------------------------------------
      -- 武功清单(93项)
      file.Next_Block;
      Kongfus_Type'Write(file.Data_Stream, From.Kongfus.all);
      -------------------------------------------------------------------
      -- 商店信息(5项)
      file.Next_Block;
      Shops_Type'Write(file.Data_Stream, From.Shops.all);
      -- if Stream_IO.Index(file.Data_File) not in 16#0001_be43# then
      --    Trace_Log("warnning, shops size not matched");
      -- end if;
      file.Close;
      Trace_Log("stored world record " & To);
   end Save_World_Record;

   procedure Save_Scene_Maps (From : Scene_Maps_Pointer; To : String)
   is
      file : Grps.Group_File_Type;
   begin
      Trace_Log("storing " & From'Length'Img & " scene maps to " & To);
      file.Open(JY_Path & To & ".IDX", JY_Path & To & ".GRP", False);
      for map of From.all loop
         file.Next_Block;
         for layer of map(ml_Earth .. ml_BY) loop
            Layer_Type'Write(file.Data_Stream, layer.all);
         end loop;
      end loop;
      file.Close;
      Trace_Log("stored scene maps " & To);
   end Save_Scene_Maps;

   procedure Save_Scene_Stories (From : Scene_Stories_Pointer; To : String)
   is
      file : Grps.Group_File_Type;
   begin
      Trace_Log("storing scene stories " & To);
      file.Open(JY_Path & To & ".IDX", JY_Path & To & ".GRP", False);
      for s of From.all loop
         file.Next_Block;
         Stories_Type'Write(file.Data_Stream, s.all);
      end loop;
      file.Close;
      Trace_Log("stored scene stories " & To);
   end Save_Scene_Stories;

end Legend.Resources;
